Skip to main content

10 进程数据结构

无论是进程还是线程,内核里统一都叫任务(Task),由一个统一的结构 task_struct 进行管理。

内核使用链表将所有的 task_struct 串起来。

任务 ID

每个任务都有一个 ID 作为这个任务的唯一标识。

pid_t pid; # process ID
pid_t tgid; # thread group ID
struct task_struct *group_leader;

任务展示

给任务下发指令

进程如果只有主线程,pid,tgid,group_leader 都指向自己。如果创建了其他线程,tgid 是进程的主线程的 pid,其他线程有自己的 pid,group_leader 指向进程的主线程。

信号处理

  • 被阻塞暂不处理(blocked)
  • 尚等待处理(pending)
  • 正在通过信号处理函数进行处理(sighand)

处理的结果有:忽略,结束进程等。下发信号时要区分进程和线程。

任务状态

TASK_RUNNING 表示进程在时刻准备运行的状态,获得时间片的时候后进入运行状态,如果没有获得时间片再次分配时间片,运行中的进程进行 I/O 操作,需要等待 I/O 完毕,这时会释放 CPU 进入睡眠状态。

TASK_INTERRUPTIBLE,可中断的睡眠状态。一种浅睡眠状态,等待 I/O 完成时来一个信号,进程会被唤醒,不是继续刚才的操作,而是进行信号处理。

TASK_UNINTERRUPTIBLE,不可中断的睡眠状态。一种深度睡眠状态,不可被信号唤醒,只能死等 I/O 操作完成。一旦 I/O 操作因为特殊原因不能完成,则无法再叫醒,除非重启。

进程调度

进程的状态切换,包含是否在运行队列,优先级,调度策略,可以使用哪些 CPU 等信息。

运行统计信息

进程在用户态和内核态消耗的时间、上下文切换的次数等。

进程亲缘关系

  • parent 指向其父进程。当它终止时,必须向它的父进程发送信号。
  • children 表示链表的头部。链表中的所有元素都是它的子进程。
  • sibling 用于把当前进程插入到兄弟链表中。

进程权限

  1. uid 和 gid:谁启动的进程,就是谁的 ID
  2. euid 和 egid:当进程要操作消息队列、共享内存、信号量等对象时,比较这个用户和组是否有权限
  3. fsuid 和 fsgid:对文件操作审核的权限

内存管理

每个进程都有自己独立的虚拟内存空间

文件与文件系统

每个进程有一个文件系统的数据结构,还有一个打开文件的数据结构

用户态函数栈

进程的内存空间里,栈从高地址到低地址,往下增长的结构,上面是栈底,下面是栈顶,入栈和出栈的操作是从下面的栈顶开始。

内核态函数栈

通过 task_struct 找内核栈

先从 task_struct 找到内核栈的开始位置,然后这个位置加上 THREAD_SIZE 到最后位置,然后转换为 struct pt_regs 再减一,减少一个 pt_regs 的位置,到这个结构的首地址。

通过内核栈找 task_struct

thread_info 结构有个成员变量 task 指向 task_struct,用 current_thread_info()->task 来获取 task_struct。thread_info 的位置就是内核栈的最高位置,减去 THREAD_SIZE 就到了 thread_info 的起始地址。